Black Friday Sale Upgrade Your Home →

Generating Containers with connect_from React Redux_VisibleTodoList

Generating Containers with connect() from React Redux (VisibleTodoList)

Now that we have react-redux, we are using its Provider component to pass store down via context.

Our container components are similar: they need to re-render when the store's state changes, they need to unsubscribe from the store when they unmount, and they take the current state from the Redux store and use it to render the presentational components with some props that they calculate.

They also need to specify the contextTypes to get the store from the context.

We're going to write VisibleTodoList in a different way now.

We'll start by writing a function mapStateToProps which takes the Redux store's state, and returns the props that we need to pass to the presentational TodoList component so it can be rendered with the current state.

In this case, TodoList only takes a single prop called todos, so we can move the expression into our mapStateToProps function. It returns the props that depend on the current state of the Redux store, which in this case is just the todos.

JAVASCRIPT
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos (
state.todos,
state.visibilityFilter
)
};
}

Now we'll create another function called mapDispatchToProps that accepts the dispatch() from store as its only argument. It returns the props that should be passed to the TodoList component that depend on the dispatch() method. In the case of TodoList, the only prop that TodoList took that requires store.dispatch() is onTodoClick. So we will remove onTodoClick from TodoList and put it into mapDispatchToProps's return. Note that we don't need the reference to store anymore, and can just change store.dispatch() to just dispatch(), which is provided as an argument to matchDispatchToProps.

JAVASCRIPT
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch({
type: 'TOGGLE_TODO',
id
})
}
};
};

Review of what we just did

We now have two functions:

A function mapStateToProps that maps the Redux store's state to the props of the TodoList component that are related to the data from the Redux store. These props will be updated any time the state changes

We also created a function mapDispatchToProps that maps the store's dispatch() method of and returns the props that use the dispatch method to dispatch actions. So it returns the callback props needed by the presentational component. It specifies the behavior of which callback prop dispatches which action.

The connect() function

Together, these two new functions describe a container component so well that instead of writing it we can generate it by using the connect() function provided by react-redux.

Now instead of creating a VisibleTodoList class, we can declare a variable and use the connect() method to obtain it. We'll pass in mapStateToProps as the first argument, and mapDispatchToProps as the second. As this is a curried function, we must call it again, passing in the presentational component that we want it to wrap and pass the props, thereby connecting to the redux store (in this case, TodoList).

The result of the connect call is the container component that is going to render the presentational component. It will calculate the props to pass to the presentational component by merging the objects returned from mapStateToProps, mapDispatchToProps, and its own props

JAVASCRIPT
const { connect } = ReactRedux;
// import { connect } from 'react-redux';
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)

The connect() function will generate the component just like the one we wrote by hand, so we don't have to write the code to subscribe to the store or to specify the context types manually.

Generating Containers with connect() from React Redux (AddTodo)

In the last section we used connect() to set up our VisibleTodoList component.

Since these examples have all of our JavaScript written in a single file, we need to rename our mapStateToProps and mapDispatchToProps functions to be more specific. Note that this doesn't need to be done if we keep all of our components in separate files.

Recall that our AddTodo component wasn't clearly a presentational or container component. However, it does rely on store for the dispatch() function.

Instead of reading store from the context, we are going to refactor AddTodo to read dispatch from the props. This is because AddTodo only needs dispatch(), not the whole store.

We will be creating a container component using connect() that will inject the dispatch function as a prop. We will remove AddTodo.contextTypes because the component generated by the connect() function will take care of reading the store from the context.

Before

JAVASCRIPT
const AddTodo = (props, { store }) => {
.
. // inside `return`
.
<button onClick={() => {
store.dispatch({
.
.
.
}
AddTodo.contextTypes = {
store: React.PropTypes.object
}

After

JAVASCRIPT
let AddTodo = ({ dispatch }) => {
.
. // inside `return`
.
<button onClick={() => {
dispatch({
.
.
.
}
// No more `AddTodo.contextTypes`

Notice that we changed const to let in our declaration. This lets us reassign AddTodo so the consuming component doesn't need to specify the dispatch prop. We don't have to specify dispatch as a prop because it will be injected by the component generated by the connect call.

The first argument to connect() is mapStateToProps, but there aren't any props for our AddTodo component that depend on the current state. Because of this, we'll have our first parameter return an empty object.

The second argument to connect() is mapDispatchToProps, but AddTodo doesn't need any callback props. Because of this, we'll just return the dispatch function itself as a prop with the same name.

We'll call the function a second time to specify the component we want to wrap (in this case AddTodo itself).

JAVASCRIPT
AddTodo = connect(
state => {
return {};
},
dispatch => {
return { dispatch };
}
)(AddTodo);

Now AddTodo won't pass any props dependent on state, but it will pass dispatch() itself as a function so that the component can read from the props and use it without worrying about context or specifying any ContextTypes.

But it's wasteful

Why subscribe to the store if we aren't going to calculate props from the state? Because we don't need to subcribe to the store, we can call connect() without mapStateToProps as an argument, instead passing in null. What this does is tell connect that there is no need to subscribe to the store.

It's a common pattern to inject just the dispatch function, so if connect() sees that the second argument is null (or any falsey value), you'll get dispatch injected as a prop.

What this means is that we can accomplish the same effect as the above code by removing the arguments from the connect function:

JAVASCRIPT
AddTodo = connect()(AddTodo)

Now the default behavior to not subscribe to the store, and inject dispatch as a prop.

  Previous      Next